/*
 * OPIAM Suite
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package opiam.admin.faare.persistence;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;


/**
  * Iterator for the simple LazyCollections.
  */
public class LazyIterator implements Iterator
{
    /** Number of modified elements. */
    private int changestamp;

    /** Current index. */
    private int cursor;

    /** Enclosing LazyStringCollection. */
    private LazyStringCollection parent;

    /**
     * Creates an iterator.
     * @param rc enclosing LazyStringCollection.
     */
    protected LazyIterator(LazyStringCollection rc)
    {
        if (!rc.isLoaded())
        {
            rc.lazyLoad();
        }

        parent = rc;
        changestamp = rc.getChangecount();
    }

    /**
     * @see java.util.Iterator#hasNext().
     */
    @Override
	public boolean hasNext()
    {
        if (changestamp != parent.getChangecount())
        {
            throw new ConcurrentModificationException(
                "Concurrent Modification is not allowed!");
        }

        if (cursor >= parent.size())
        {
            return false;
        }

        return true;
    }

    /**
     * @see java.util.Iterator#next()
     */
    @Override
	public Object next()
    {
        if (changestamp != parent.getChangecount())
        {
            throw new ConcurrentModificationException(
                "Concurrent Modification is not allowed!");
        }

        if (cursor >= parent.size())
        {
            throw new NoSuchElementException("Read after the end of iterator!");
        }

        Object s;

        if (cursor < parent.getAdded().size())
        {
            s = parent.getAdded().get(cursor++);

            return s;
        }
        else
        {
            // skip to the first "not deleted" dn
            int j = cursor++ - parent.getAdded().size();

            s = parent.getValues().get(j);

            int k = 0;

            while (isSkipped(s))
            {
                hasNext();
                k = cursor++ - parent.getAdded().size();

                if (k < parent.getValues().size())
                {
                    s = parent.getValues().get(k);
                }
                else
                {
                    return null;
                }
            }

            return s;
        }
    }

    /**
     * Checks whether given value is being deleted or not.
     * @param id Value to check.
     * @return True or false.
     */
    protected boolean isSkipped(Object id)
    {
        if (parent.getDeleted().contains(id))
        {
            return true;
        }
        return false;
    }

    /**
     * @see java.util.Iterator#remove().
     */
    @Override
	public void remove()
    {
        if (cursor <= 0)
        {
            throw new IllegalStateException(
                "Method next() must be called before remove!");
        }

        if (changestamp != parent.getChangecount())
        {
            throw new ConcurrentModificationException(
                "Concurrent Modification is not allowed!");
        }

        Object s;
        cursor--;

        if (cursor < parent.getAdded().size())
        {
            parent.getAdded().remove(cursor);
            parent.decSize();
            parent.incChangecount();
            changestamp = parent.getChangecount();
        }
        else
        {
            // backward to the first not deleted ids
            s = parent.getValues().get(cursor);

            while (parent.getDeleted().contains(s))
            {
                s = parent.getValues().get(cursor--);
            }

            if (cursor < parent.getAdded().size())
            {
                parent.getAdded().remove(s);
                parent.decSize();
                parent.incChangecount();
                changestamp = parent.getChangecount();
            }
            else
            {
                parent.getDeleted().add(s);
                parent.decSize();
                parent.incChangecount();
                changestamp = parent.getChangecount();
            }
        }
    }

    /**
     * Gets number of modified elements.
     * @return number of modified elements.
     */
    protected int getChangestamp()
    {
        return changestamp;
    }

    /**
     * Gets current index.
     * @return current index.
     */
    protected int getCursor()
    {
        return cursor;
    }

    /**
     * Increments cursor.
     */
    protected void incCursor()
    {
        cursor++;
    }

    /**
     * Gets enclosing LazyStringCollection.
     * @return enclosing collection.
     */
    protected LazyStringCollection getParent()
    {
        return parent;
    }
}
